package com.wideplay.warp.clustering;

import com.google.inject.Key;

import java.lang.annotation.Annotation;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * On: 23/06/2007
 *
 * @author Dhanji R. Prasanna
 * @since 1.0
 */
public abstract class DataGridService {
    private final ConcurrentMap<Class<? extends Annotation>, DataGrid> spaces = new ConcurrentHashMap<Class<? extends Annotation>, DataGrid>();
    protected final List<Class<? extends Annotation>> spaceNames;

    private static DataGridService instance;

    protected DataGridService(List<Class<? extends Annotation>> spaces) {
        this.spaceNames = spaces;

        instance = this;
    }

    public abstract void start();

    public void synchronizeAll() {
        spaces.get(GridScoped.class).flush();
    }

    public <T> void synchronize(Key<T> key) {
        spaces.get(GridScoped.class).flush(key);
    }

    protected void register(Class<? extends Annotation> spaceName, DataGrid space) {
        spaces.putIfAbsent(spaceName, space);
    }

    /**
     * Removes an object from (specified) scope(s) if it exists. If no scope annotations are specified, it is
     * assumed that we're working in single-cluster mode (i.e. bound to @GridScoped annotation). If working in
     * single-cluster mode and scope annotations are provided they will be silently ignored.
     *
     * @param key The key whose instance to descope from the cluster(s).
     * @param spaceKeys Any number of bound scope annotations (using .cluster() argument to the DataGridService builder)
     *  identifying the cluster to remove the instance from.
     *  
     * @throws IllegalArgumentException If in multi-cluster mode and no scope annotations are provided
     */
    public <T> void descope(Key<T> key, Class<? extends Annotation>... spaceKeys) {
        if (spaceNames.isEmpty()) {
            spaces.get(GridScoped.class).descope(key);
            
            return;
        }
        
        if (!spaceNames.isEmpty() && spaceKeys.length == 0)
            throw new IllegalArgumentException("Multiple cluster spaces are active, you must specify a cluster's individual scope-annotation to descope from it");

        //ok descope from all specified cluster spaces
        for (Class<? extends Annotation> spaceKey : spaceKeys) {
            final DataGrid dataGrid = spaces.get(spaceKey);

            if (null == dataGrid)
                throw new IllegalArgumentException("No such cluster bound to scope annotation: " + spaceKey);
                
            dataGrid.descope(key);
        }
    }

    /**
     * See the declaration of descope().
     *
     * @param clazz The class whose instance to descope
     * @param spaceKeys scope annotations
     */
    public <T> void descope(Class<T> clazz, Class<? extends Annotation>... spaceKeys) {
        descope(Key.get(clazz), spaceKeys);
    }

    public static ClusterServiceBuilder usingSwarm() {
        return new DataGridServiceBuilderImpl(DataGridModule.ClusterCacheVendor.SWARMCACHE);
    }

    static DataGrid getCluster(Class<? extends Annotation> name) {
        return instance.spaces.get(name);
    }

    public static ClusterServiceBuilder usingOsCache() {
        return new DataGridServiceBuilderImpl(DataGridModule.ClusterCacheVendor.OSCACHE);
    }

    //ugly wrapper because type-literals on wildcard types are not working =(
    protected static class SpacesListWrapper {
        private final List<Class<? extends Annotation>> spaceNames;

        public SpacesListWrapper(List<Class<? extends Annotation>> spaceNames) {
            this.spaceNames = spaceNames;
        }

        public List<Class<? extends Annotation>> getSpaceNames() {
            return spaceNames;
        }
    }
}
